/**
 * Created by toor on 17-10-10.
 */
import Vue from 'vx';
import $locale from '../service/$locale';

var formats = $locale.NUMBER_FORMATS;
var toString = Object.prototype.toString();
var DECIMAL_SEP = ".",
	DATE_FORMATS = {
		yyyy: dateGetter("FullYear", 4),
		yy: dateGetter("FullYear", 2, 0, !0),
		y: dateGetter("FullYear", 1),
		MMMM: dateStrGetter("Month"),
		MMM: dateStrGetter("Month", !0),
		MM: dateGetter("Month", 2, 1),
		M: dateGetter("Month", 1, 1),
		dd: dateGetter("Date", 2),
		d: dateGetter("Date", 1),
		HH: dateGetter("Hours", 2),
		H: dateGetter("Hours", 1),
		hh: dateGetter("Hours", 2, -12),
		h: dateGetter("Hours", 1, -12),
		mm: dateGetter("Minutes", 2),
		m: dateGetter("Minutes", 1),
		ss: dateGetter("Seconds", 2),
		s: dateGetter("Seconds", 1),
		sss: dateGetter("Milliseconds", 3),
		EEEE: dateStrGetter("Day"),
		EEE: dateStrGetter("Day", !0),
		a: ampmGetter,
		Z: timeZoneGetter
	}, DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
	NUMBER_STRING = /^\-?\d+$/;
function isUndefined(value) {
	return "undefined" == typeof value
}

function isDefined(value) {
	return "undefined" != typeof value
}

function isObject(value) {
	return null != value && "object" == typeof value
}

function isString(value) {
	return "string" == typeof value
}

function isNumber(value) {
	return "number" == typeof value
}

function isDate(value) {
	return "[object Date]" == toString.apply(value)
}

function isArray(value) {
	return "[object Array]" == toString.apply(value)
}

function isFunction(value) {
	return "function" == typeof value
}

function int(str) {
	return parseInt(str, 10)
}
function toBoolean(value) {
	if (value && 0 !== value.length) {
		var v = lowercase("" + value);
		value = !("f" == v || "0" == v || "false" == v || "no" == v || "n" == v || "[]" == v)
	} else value = !1;
	return value
}
var lowercase = function (string) {
	return isString(string) ? string.toLowerCase() : string
}, uppercase = function (string) {
	return isString(string) ? string.toUpperCase() : string
};

function currencyFilter($locale) {
	var formats = $locale.NUMBER_FORMATS;
	return function (amount, currencySymbol) {
		return isUndefined(currencySymbol) && (currencySymbol = formats.CURRENCY_SYM), formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2).replace(/\u00A4/g, currencySymbol)
	}
}
function numberFilter($locale) {
	var formats = $locale.NUMBER_FORMATS;
	return function (number, fractionSize) {
		return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize)
	}
}

function dateFilter($locale) {
	function jsonStringToDate(string) {
		var match;
		if (match = string.match(R_ISO8601_STR)) {
			var date = new Date(0),
				tzHour = 0,
				tzMin = 0,
				dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
				timeSetter = match[8] ? date.setUTCHours : date.setHours;
			match[9] && (tzHour = int(match[9] + match[10]), tzMin = int(match[9] + match[11])), dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
			var h = int(match[4] || 0) - tzHour,
				m = int(match[5] || 0) - tzMin,
				s = int(match[6] || 0),
				ms = Math.round(1e3 * parseFloat("0." + (match[7] || 0)));
			return timeSetter.call(date, h, m, s, ms), date
		}
		return string
	}

	var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
	return function (date, format) {
		var fn, match, text = "",
			parts = [];
		if (format = format || "mediumDate", format = $locale.DATETIME_FORMATS[format] || format, isString(date) && (date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date)), isNumber(date) && (date = new Date(date)), !isDate(date)) return date;
		for (; format;) match = DATE_FORMATS_SPLIT.exec(format), match ? (parts = concat(parts, match, 1), format = parts.pop()) : (parts.push(format), format = null);
		parts.forEach(function (value) {
			fn = DATE_FORMATS[value], text += fn ? fn(date, $locale.DATETIME_FORMATS) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'")
		});
		return text;
	}
}

function jsonFilter() {
	return function (object) {
		return JSON.stringify(object);
	}
}

function limitToFilter() {
	return function (input, limit) {
		if (!isArray(input) && !isString(input)) return input;
		if (limit = int(limit), isString(input)) return limit ? limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length) : "";
		var i, n, out = [];
		for (limit > input.length ? limit = input.length : limit < -input.length && (limit = -input.length), limit > 0 ? (i = 0, n = limit) : (i = input.length + limit, n = input.length); n > i; i++) out.push(input[i]);
		return out
	}
}

function orderByFilter($parse) {
	return function (array, sortPredicate, reverseOrder) {
		function comparator(o1, o2) {
			for (var i = 0; i < sortPredicate.length; i++) {
				var comp = sortPredicate[i](o1, o2);
				if (0 !== comp) return comp
			}
			return 0
		}

		function reverseComparator(comp, descending) {
			return toBoolean(descending) ? function (a, b) {
					return comp(b, a)
				} : comp
		}

		function compare(v1, v2) {
			var t1 = typeof v1,
				t2 = typeof v2;
			return t1 == t2 ? ("string" == t1 && (v1 = v1.toLowerCase(), v2 = v2.toLowerCase()), v1 === v2 ? 0 : v2 > v1 ? -1 : 1) : t2 > t1 ? -1 : 1
		}

		if (!isArray(array)) return array;
		if (!sortPredicate) return array;
		sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate], sortPredicate = map(sortPredicate, function (predicate) {
			var descending = !1,
				get = predicate || identity;
			return isString(predicate) && (("+" == predicate.charAt(0) || "-" == predicate.charAt(0)) && (descending = "-" == predicate.charAt(0), predicate = predicate.substring(1)), get = $parse(predicate)), reverseComparator(function (a, b) {
				return compare(get(a), get(b))
			}, descending)
		});
		for (var arrayCopy = [], i = 0; i < array.length; i++) arrayCopy.push(array[i]);
		return arrayCopy.sort(reverseComparator(comparator, reverseOrder))
	}
}


Vue.filter('currency', currencyFilter($locale));
Vue.filter('number', numberFilter($locale));
Vue.filter('date', dateFilter($locale));
Vue.filter('json', jsonFilter());
Vue.filter('limitTo', limitToFilter());
Vue.filter('orderBy', orderByFilter());
Vue.filter('lowercase', lowercase);
Vue.filter('uppercase', uppercase);

function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
	if (isNaN(number) || !isFinite(number)) return "";
	var isNegative = 0 > number;
	number = Math.abs(number);
	var numStr = number + "",
		formatedText = "",
		parts = [],
		hasExponent = !1;
	if (-1 !== numStr.indexOf("e")) {
		var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
		match && "-" == match[2] && match[3] > fractionSize + 1 ? numStr = "0" : (formatedText = numStr, hasExponent = !0)
	}
	if (hasExponent) fractionSize > 0 && number > -1 && 1 > number && (formatedText = number.toFixed(fractionSize));
	else {
		var fractionLen = (numStr.split(formats.DECIMAL_SEP)[1] || "").length;
		typeof fractionSize == undefined && (fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac));
		var pow = Math.pow(10, fractionSize);
		number = Math.round(number * pow) / pow;
		var fraction = ("" + number).split(formats.DECIMAL_SEP),
			whole = fraction[0];
		fraction = fraction[1] || "";
		var i, pos = 0,
			lgroup = pattern.lgSize,
			group = pattern.gSize;
		if (whole.length >= lgroup + group)
			for (pos = whole.length - lgroup, i = 0; pos > i; i++)(pos - i) % group === 0 && 0 !== i && (formatedText += groupSep), formatedText += whole.charAt(i);
		for (i = pos; i < whole.length; i++)(whole.length - i) % lgroup === 0 && 0 !== i && (formatedText += groupSep), formatedText += whole.charAt(i);
		for (; fraction.length < fractionSize;) fraction += "0";
		fractionSize && "0" !== fractionSize && (formatedText += decimalSep + fraction.substr(0, fractionSize))
	}
	return parts.push(isNegative ? pattern.negPre : pattern.posPre), parts.push(formatedText), parts.push(isNegative ? pattern.negSuf : pattern.posSuf), parts.join("")
}

function padNumber(num, digits, trim) {
	var neg = "";
	for (0 > num && (neg = "-", num = -num), num = "" + num; num.length < digits;) num = "0" + num;
	return trim && (num = num.substr(num.length - digits)), neg + num
}

function dateGetter(name, size, offset, trim) {
	return offset = offset || 0,
		function (date) {
			var value = date["get" + name]();
			return (offset > 0 || value > -offset) && (value += offset), 0 === value && -12 == offset && (value = 12), padNumber(value, size, trim)
		}
}

function dateStrGetter(name, shortForm) {
	return function (date, formats) {
		var value = date["get" + name](),
			get = uppercase(shortForm ? "SHORT" + name : name);
		return formats[get][value]
	}
}

function timeZoneGetter(date) {
	var zone = -1 * date.getTimezoneOffset(),
		paddedZone = zone >= 0 ? "+" : "";
	return paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2)
}
function ampmGetter(date, formats) {
	return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]
}